using System;
using System.Collections.Generic;
using System.Linq;

namespace PingPong.GameEngine.Components
{

	public class Team : IList<Player>, IScorable<Team>
	{

		#region Members

		/// <summary>
		/// The collection.
		/// </summary>
		private Lazy<List<Player>> collection;

		/// <summary>
		/// The score.
		/// </summary>
		private Lazy<Score> score;

		/// <summary>
		/// The type of the team rotation.
		/// </summary>
		private Lazy<TeamRotationType> teamRotationType;

		#endregion

		#region Properties

		/// <summary>
		/// Gets the Collection.
		/// </summary>
		/// <value>
		/// The collection.
		/// </value>
		private List<Player> Collection
		{
			get
			{
				return this.collection.Value;
			}
		}

		/// <summary>
		/// Gets the type of the team rotation.
		/// </summary>
		/// <value>
		/// The type of the team rotation.
		/// </value>
		public TeamRotationType TeamRotationType
		{
			get
			{
				return this.teamRotationType.Value;
			}
		}

		#endregion

		#region Constructors

		/// <summary>
		/// Initializes a new instance of the <see cref="PingPong.GameEngine.Components.Team"/> class.
		/// </summary>
		public Team()
		{
			this.collection = new Lazy<List<Player>>(() => new List<Player>());
			this.score = new Lazy<Score>(() => new Score());
		}

		#endregion

		#region Methods

		/// <summary>
		/// Gets the player.
		/// </summary>
		/// <returns>
		/// The player.
		/// </returns>
		/// <param name='name'>
		/// Name.
		/// </param>
		public Player GetPlayer(string name)
		{
			if (string.IsNullOrWhiteSpace(name))
			{
				throw new ArgumentNullException("name");
			}
			
			return this.Where(player => player.Name.Equals(
				name,
				StringComparison.OrdinalIgnoreCase
			)
			)
            .FirstOrDefault();
		}

		/// <summary>
		/// Sets the type of the team rotation.
		/// </summary>
		/// <returns>
		/// The team rotation type.
		/// </returns>
		/// <param name='teamRotationType'>
		/// Team rotation type.
		/// </param>
		public Team SetTeamRotationType(TeamRotationType teamRotationType)
		{
			this.teamRotationType = new Lazy<TeamRotationType>(() => teamRotationType);
			return this;
		}

		/// <summary>
		/// Rotates the team.
		/// </summary>
		/// <returns>
		/// The team.
		/// </returns>
		public Team RotateTeam()
		{
			switch (this.TeamRotationType)
			{
				case TeamRotationType.RoundRobin:
					PerformRoundRobinRotation();
					break;
				case TeamRotationType.Random:
					PerformRandomRotation();
					break;
				case TeamRotationType.None:
				default:
					break;
			}
			
			return this;
		}

		/// <summary>
		/// Performs the round robin rotation.
		/// </summary>
		private void PerformRoundRobinRotation()
		{
			if (this.Collection.Any())
			{
				var player = this.Collection.LastOrDefault();
				var lastIndex = this.Collection.Count - 1;
				this.Collection.RemoveAt(lastIndex);
				
				this.Collection.Insert(0,player);
			}
			
		}

		/// <summary>
		/// Performs the random rotation.
		/// </summary>
		private void PerformRandomRotation()
		{
			if (this.Collection.Any())
			{
				this.collection = new Lazy<List<Player>>(() => this.Collection.Select(player => new { GUID = new Guid(), Player = player })
				                                                              .OrderBy(anon => anon.GUID)
				                                                              .Select(anon => anon.Player)
				                                                              .ToList()
				);
			}
		}

		#endregion

		#region IEnumerable implementation

		/// <summary>
		/// Gets the enumerator.
		/// </summary>
		/// <returns>
		/// The enumerator.
		/// </returns>
		public System.Collections.IEnumerator GetEnumerator()
		{
			return this.Collection.GetEnumerator();
		}

		#endregion

		#region IEnumerable implementation

		/// <summary>
		/// Gets the enumerator.
		/// </summary>
		/// <returns>
		/// The enumerator.
		/// </returns>
		IEnumerator<Player> IEnumerable<Player>.GetEnumerator()
		{
			return this.Collection.GetEnumerator();
		}

		#endregion

		#region ICollection implementation

		/// <summary>
		/// Add the specified item.
		/// </summary>
		/// <param name="item">Item.</param>
		void ICollection<Player>.Add(Player item)
		{
			Add(item);
		}

		/// <summary>
		/// Add the specified item.
		/// </summary>
		/// <param name="item">Item.</param>
		public Team Add(Player item)
		{
			this.Collection.Add(item);
			return this;
		}

		/// <summary>
		/// Add the specified items.
		/// </summary>
		/// <param name="items">Items.</param>
		public Team Add(IEnumerable<Player> items)
		{
			foreach(var item in items)
			{
				this.Collection.Add(item);
			}
			return this;
		}

		/// <summary>
		/// Clear this instance.
		/// </summary>
		void ICollection<Player>.Clear()
		{
			Clear();
		}

		public Team Clear()
		{
			this.Collection.Clear();
			return this;
		}

		/// <summary>
		/// Contains the specified item.
		/// </summary>
		/// <param name="item">Item.</param>
		public bool Contains(Player item)
		{
			return this.Collection.Contains(item);
		}

		/// <summary>
		/// Copies to.
		/// </summary>
		/// <param name="array">Array.</param>
		/// <param name="arrayIndex">Array index.</param>
		public void CopyTo(Player[] array, int arrayIndex)
		{
			this.Collection.CopyTo(array,arrayIndex);
		}

		/// <summary>
		/// Remove the specified item.
		/// </summary>
		/// <param name="item">Item.</param>
		public bool Remove(Player item)
		{
			return this.Collection.Remove(item);
		}

		/// <summary>
		/// Gets the count.
		/// </summary>
		/// <value>The count.</value>
		public int Count
		{
			get
			{
				return this.Collection.Count;
			}
		}

		/// <summary>
		/// Gets a value indicating whether this instance is read only.
		/// </summary>
		/// <value><c>true</c> if this instance is read only; otherwise, <c>false</c>.</value>
		public bool IsReadOnly
		{
			get
			{
				return false;
			}
		}

		#endregion

		#region IScorable implementation

		/// <summary>
		/// Gets the score.
		/// </summary>
		/// <value>The score.</value>
		public Score Score
		{
			get
			{
				return this.score.Value;
			}
		}

		/// <summary>
		/// Sets the score.
		/// </summary>
		/// <returns>The score.</returns>
		/// <param name="score">Score.</param>
		public Team SetScore(Score score)
		{
			this.score = new Lazy<Score>(() => score);
			return this;
		}

		#endregion

		#region IList implementation

		/// <summary>
		/// Indexs the of.
		/// </summary>
		/// <returns>The of.</returns>
		/// <param name="item">Item.</param>
		public int IndexOf(Player item)
		{
			return this.Collection.IndexOf(item);
		}

		/// <summary>
		/// Insert the specified index and item.
		/// </summary>
		/// <param name="index">Index.</param>
		/// <param name="item">Item.</param>
		void IList<Player>.Insert(int index, Player item)
		{
			Insert(index,item);
		}

		/// <summary>
		/// Insert the specified index and item.
		/// </summary>
		/// <param name="index">Index.</param>
		/// <param name="item">Item.</param>
		public Team Insert(int index, Player item)
		{
			this.Collection.Insert(index,item);
			return this;
		}

		/// <summary>
		/// Removes at index.
		/// </summary>
		/// <param name="index">Index.</param>
		void IList<Player>.RemoveAt(int index)
		{
			RemoveAt(index);
		}

		/// <summary>
		/// Removes at index.
		/// </summary>
		/// <returns>The <see cref="PingPong.GameEngine.Components.Team"/>.</returns>
		/// <param name="index">Index.</param>
		public Team RemoveAt(int index)
		{
			this.Collection.RemoveAt(index);
			return this;
		}

		/// <summary>
		/// Gets or sets the element at the specified index.
		/// </summary>
		/// <returns>The element at the specified index.</returns>
		/// <param name="index">The zero-based index of the element to get or set.</param>
		public Player this [int index]
		{
			get
			{
				return this.Collection[index];
			}
			set
			{
				this.Collection[index] = value;
			}
		}

		#endregion

	}
}

